#include "uart.hpp"
#include <iostream>
#include <cstring>
#include <chrono>
#include <ctime>

uart::uart(const std:: string &port_name,int baud_rate,bool ctsrts):port_name(port_name),baud_rate(baud_rate),ctsrts(ctsrts),fd(-1),running(false)
{

}

uart::~uart()
{
    this->close_port();
}

void uart::open_port()
{
    this->fd = open(port_name.c_str(),O_RDWR | O_NOCTTY | O_NONBLOCK);
    
    if(fd < 0){
        throw std::runtime_error("Failed to open prot");
    }else{
        this->configure_port(); 
        this->running = true;
        this->read_thread = std::thread(&uart::read_loop,this);
    }

}


void uart::configure_port()
{
    memset(&this->tty,0,sizeof tty);

    if(tcgetattr(this->fd,&this->tty) != 0){
        close(fd);
        throw std::runtime_error("Failed to get prot attributes");
    }else{
        cfmakeraw(&this->tty);
        this->tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
        this->tty.c_iflag &= ~IGNBRK;
        this->tty.c_lflag = 0;
        this->tty.c_oflag = 0;
        this->tty.c_cc[VMIN] = 1;
        this->tty.c_cc[VTIME] = 5;
        this->tty.c_cflag |= (CLOCAL | CREAD);
        
        if(this->ctsrts){
            this->tty.c_cflag |= CRTSCTS;
        }else{
            this->tty.c_cflag &= ~CRTSCTS;
        }
        
        this->tty.c_cflag &= ~(PARENB | PARODD);
        this->tty.c_cflag &= ~CSTOPB;

        cfsetispeed(&this->tty,this->baud_rate);
        cfsetospeed(&this->tty,this->baud_rate);

        if(tcsetattr(fd,TCSANOW,&this->tty) != 0){
            close(fd);
            throw std::runtime_error("Failed to set port attributes");
        }
    }
}

void uart::close_port()
{
    //running = false;

    if(this->read_thread.joinable()){
        this->read_thread.join();
    }
    
    if(this->fd >= 0){
        close(fd);
        fd = -1;
    }

	std::cout << "close port" << std::endl;
}

void uart::write_data(const std::vector<uint8_t> &data){
	if(this->fd < 0){
        throw std::runtime_error("Port is not open");
    }else{	
        ssize_t n = write(this->fd,data.data(),data.size());
        if(n != static_cast<ssize_t>(data.size())){
            throw std::runtime_error("Failed to write data");    
		}
    }
}


void uart::set_data_callback(data_callback_t callback){
    this->data_callback = callback;
}


void uart::read_loop()
{
    fd_set readfds;
    std::vector<uint8_t>buffer(2048);
	struct timeval timeout;
	auto currenttime = std::chrono::system_clock::now();
	std::time_t currenttime_t;

    while(running){
        FD_ZERO(&readfds);
        FD_SET(this->fd,&readfds);
		timeout.tv_sec = 5;
		timeout.tv_usec = 0;
		currenttime   = std::chrono::system_clock::now();
		currenttime_t = std::chrono::system_clock::to_time_t(currenttime);
		
		int select_result = select(this->fd+1,&readfds,NULL,NULL,&timeout);
        if(select_result > 0 && FD_ISSET(fd,&readfds)){
			ssize_t n = read(this->fd,buffer.data(),buffer.size());
        	if(n >0){
            	buffer.resize(n);
            	if(this->data_callback){
               		this->data_callback(buffer); 
				}
            	this->write_data(buffer);
        	}else{
            	buffer.resize(2048);
        	}
		}else if(select_result == 0){
			std::cout << "uart Alive:" << std::ctime(&currenttime_t) << std::endl;
		}

    }

}




























